package com.ruoyi.web.controller.system;

import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfig;
import com.github.wxpay.sdk.WXPayConstants;
import com.ruoyi.back.domain.OrderDetail;
import com.ruoyi.back.service.IOrderDetailService;
import com.ruoyi.common.config.MyWXPayConfig;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.PayUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.system.service.impl.WXPayClient;
import io.netty.handler.codec.base64.Base64;
import io.swagger.annotations.ApiOperation;
import org.aspectj.weaver.ast.Or;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName WXPayPrecreateController
 * @Description TODO
 * @Author luguanqing
 * @Date 2021/4/25 11:11
 * @Version 1.0
 */
@RestController
@RequestMapping("/pay")
public class WXPayPrecreateController {

    @Autowired
    private IOrderDetailService orderDetailService;

    /**
     * 扫码支付 - 统一下单
     *
     * <a href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1">
     * 扫码支付API</a>
     */
    @ApiOperation(value = "createCode", notes = "返回支付二维码")
    @PostMapping("/common/createCode")
    public AjaxResult precreate(@Validated @RequestBody OrderDetail orderDetail, HttpServletResponse response)
            throws Exception {
        MyWXPayConfig myWXPayConfig = new MyWXPayConfig() {
        };
        WXPay wxPay = new WXPay(myWXPayConfig);
        Map<String, String> reqData = new HashMap<>();
        reqData.put("appid", myWXPayConfig.getAppID());
        reqData.put("mch_id", myWXPayConfig.getMchID());
        // 订单号
        reqData.put("out_trade_no", orderDetail.getOrderNo());
        reqData.put("trade_type", "NATIVE");
        reqData.put("product_id", StringUtils.replace(UUID.randomUUID().toString(), "-", ""));
        reqData.put("body", orderDetail.getNote());
        // 交易结束时间
         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
         reqData.put("time_expire", dateFormat.format(System.currentTimeMillis()+20*60*1000L));
        // 订单总金额，单位为分
        reqData.put("total_fee", orderDetail.getOrderMoney());
        // APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP。
        reqData.put("spbill_create_ip", "106.14.133.81");
        // 异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。
        reqData.put("notify_url", myWXPayConfig.getNotifyUrl());
        // 自定义参数, 可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"
        reqData.put("device_info", "WEB");
        // 附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用。
        reqData.put("attach", "");
        // sign签名
        reqData.put("sign", myWXPayConfig.getKey());
        // 随机字符串
        reqData.put("nonce_str", StringUtils.replace(UUID.randomUUID().toString(), "-", ""));
        Map<String, String> responseMap = wxPay.unifiedOrder(reqData);
        System.out.println(responseMap);
        String returnCode = responseMap.get("return_code");
        String resultCode = responseMap.get("result_code");
        orderDetail.setOrderStatus("USERPAYING");
        orderDetail.setId(StringUtils.replace(UUID.randomUUID().toString(), "-", ""));
        orderDetail.setPaymentWay("wx");
        orderDetailService.insertOrderDetail(orderDetail);
        if (WXPayConstants.SUCCESS.equals(returnCode)
                && WXPayConstants.SUCCESS.equals(resultCode)) {
            String prepayId = responseMap.get("prepay_id");
            String codeUrl = responseMap.get("code_url");
            BufferedImage image = PayUtil.getQRCodeImge(codeUrl);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
            ImageIO.write(image, "png", baos);//写入流中
            byte[] bytes = baos.toByteArray();//转换成字节
            BASE64Encoder encoder = new BASE64Encoder();
            String png_base64 = encoder.encodeBuffer(bytes).trim();//转换成base64串
            png_base64 = png_base64.replaceAll("\n", "").replaceAll("\r", "");
            return AjaxResult.success(png_base64);
        }
        return AjaxResult.error("获取支付二维码失败");

    }

    /**
     *
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping("/common/notify")
    public AjaxResult precreateNotify(HttpServletRequest request,
                                      HttpServletResponse response) throws Exception {
        MyWXPayConfig myWXPayConfig = new MyWXPayConfig() {
        };
        WXPayClient wxPayClient = new WXPayClient(myWXPayConfig, WXPayConstants.HMACSHA256, false);
        Map<String, String> reqData = wxPayClient.getNotifyParameter(request);

        /**
         * { transaction_id=4200000138201806180751222945,
         * nonce_str=aaaf3fe4d3aa44d8b245bc6c97bda7a8, bank_type=CFT,
         * openid=xxx, sign=821A5F42F5E180ED9EF3743499FBCF13, fee_type=CNY,
         * mch_id=xxx, cash_fee=1, out_trade_no=186873223426017, appid=xxx,
         * total_fee=1, trade_type=NATIVE, result_code=SUCCESS,
         * time_end=20180618131247, is_subscribe=N, return_code=SUCCESS }
         */
        // 特别提醒：商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致，防止数据泄漏导致出现“假通知”，造成资金损失。
        WXPay wxPay = new WXPay(myWXPayConfig);
        boolean signatureValid = wxPay.isPayResultNotifySignatureValid(reqData);
        if (signatureValid) {
            /**
             * 注意：同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
             * 推荐的做法是，当收到通知进行处理时，首先检查对应业务数据的状态，
             * 判断该通知是否已经处理过，如果没有处理过再进行处理，如果处理过直接返回结果成功。
             * 在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱。
             */
            System.out.println(reqData);
            //插入订单表中
            orderDetailService.updateOrderStatus(reqData.get("result_code"), reqData.get("out_trade_no"));
            return AjaxResult.success(reqData.get("result_code"));
        }
        return AjaxResult.error();
    }
}

